package auth

import (
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"go-admin-official/models"
	"go-admin-official/modules/config"
	"go-admin-official/modules/logger"
	"time"
)

type jwtManager struct {
	secret string
	exp    time.Duration
	alg    string
}

var lib = new(jwtManager)

func InitJWT() {
	lib = &jwtManager{
		secret: config.Get().AppSecret,
		exp:    time.Second * time.Duration(config.Get().TokenSessionTime),
		alg:    "HS256",
	}
}

const GinContextUserKey = "auth_user"

func GetUserFromContext(c *gin.Context) (user models.UsersModel, ok bool) {
	var token string
	if len(c.FullPath()) > 4 && c.FullPath()[:4] == "/api" {
		token = c.GetHeader(SessionKey)
	} else {
		var exist bool
		session := sessions.Default(c)
		token, exist = session.Get(SessionKey).(string)
		if !exist {
			return
		}
	}
	jwtToken, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
		b := []byte(lib.secret)
		return b, nil
	})
	if err != nil {
		logger.Info("Filter token parse error: ", err, ", token: ", token)
		return
	}

	if claims, exist := jwtToken.Claims.(jwt.MapClaims); exist && jwtToken.Valid {
		user = models.Users()
		user.Name = claims["name"].(string)
		user.Id = int64(claims["uid"].(float64))
		user.Email = claims["email"].(string)
		ok = true
		return
	}

	return
}

func Filter(c *gin.Context) (user models.UsersModel, ok bool) {
	if user, ok = GetUserFromContext(c); ok {
		c.Set(GinContextUserKey, map[string]interface{}{
			"user": user,
		})
		return
	}
	return
}

func FilterRefresh(c *gin.Context) (user models.UsersModel, ok bool) {
	if user, ok = GetUserFromContext(c); ok {
		user = user.Refresh()
		c.Set(GinContextUserKey, map[string]interface{}{
			"user": user,
		})
		return
	}
	return
}

func Auth(c *gin.Context) models.UsersModel {
	user, _ := c.Get(GinContextUserKey)
	return user.(map[string]interface{})["user"].(models.UsersModel)
}

var timeZone, _ = time.LoadLocation("Asia/Shanghai")

func getToken(user models.UsersModel) string {
	token := jwt.New(jwt.GetSigningMethod(lib.alg))
	token.Claims = jwt.MapClaims{
		"uid":   user.Id,
		"name":  user.Name,
		"email": user.Email,
		"exp":   time.Now().In(timeZone).Add(lib.exp).Unix(),
	}
	tokenString, err := token.SignedString([]byte(lib.secret))
	if err != nil {
		return ""
	}

	return tokenString
}
